封装
Java 的封装特性是面向对象编程的核心原则之一,它指的是将对象的状态(数据)和行为(方法)封装在一个类内部,并通过公开的接口与外部进行交互。封装的主要目的是隐藏对象的内部实现细节,只暴露必要的功能,从而保护数据的完整性和减少系统的复杂性。
扩展知识
基本概念:
- 数据隐藏:通过将类的字段(成员变量)声明为
private或protected,避免直接被外部访问。只有通过类提供的公共方法(如 getter 和 setter)才能访问和修改这些字段。 - 公共接口:通过公共方法(如 getter 和 setter)提供访问对象数据的方式。这样可以对数据进行控制和验证,确保数据的一致性和合法性。
- 保护数据:封装通过限制对数据的直接访问,减少了对对象状态的不安全修改和潜在的错误。
示例代码:
public class Person {
// 私有字段
private String name;
private int age;
// 公共构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 公共 getter 方法
public String getName() {
return name;
}
// 公共 setter 方法
public void setName(String name) {
this.name = name;
}
// 公共 getter 方法
public int getAge() {
return age;
}
// 公共 setter 方法
public void setAge(int age) {
if (age > 0) {
this.age = age;
} else {
throw new IllegalArgumentException("Age must be positive");
}
}
}
扩展知识:
封装的好处:
- 数据保护:通过隐藏数据和提供受控的访问方法,可以防止外部代码对数据进行不合法的修改。
- 维护性:封装使得对象的内部实现与外部接口分离,可以更容易地对内部实现进行更改,而不影响外部使用者。
- 简化接口:提供简洁的公共接口,减少外部代码对类的复杂性理解,从而降低系统的耦合度。
- 代码复用:通过封装,类可以重用已有的功能而不必重新实现,有助于构建模块化和可维护的代码。
访问控制修饰符:
private:只允许类内部访问,无法被外部访问。protected:允许同一包内的类以及子类访问。public:允许任何类访问。- 默认(包级别):只允许同一包内的类访问。
补充:封装的更高形态——不可变对象
getter/setter 只是封装的入门。封装更进一步,是干脆不给 setter、不让状态可变——创建之后对象完全不可修改(immutable):
public final class Money {
private final long cents; // final 字段,只能在构造时赋值
private final String currency;
public Money(long cents, String currency) {
this.cents = cents;
this.currency = currency;
}
public Money add(Money other) {
// 不修改自己,返回新对象
return new Money(this.cents + other.cents, this.currency);
}
}
不可变对象天然线程安全(没有可改的状态,就不会有并发修改问题),也可以放心地被缓存和共享——String 就是 JDK 里最著名的不可变类。Integer、LocalDate 同理。
Java 16 引入的
record是不可变数据载体的语法糖:record Point(int x, int y) {}一行顶过去几十行,字段自动private final,配套的构造器、访问器、equals/hashCode 全部自动生成。它就是"封装 + 不可变"思想的官方封装版。
补充:什么时候 setter 也是坏味道
一个简单的自查标准:setter 是否让对象在非法状态下存活。比如:
Person p = new Person();
p.setName("张三");
// 此时 age 还没设,p 处于"半成品"状态,却已经能被使用
p.setAge(-5); // 非法状态悄悄通过
更好的做法是用构造器强制"诞生即合法",setter 只保留给确实需要中途变更的字段。封装的本质不是"生成一堆 getter/setter",而是把"保证对象始终处于合法状态"的责任关在类内部。
⬅️ OOP 特性 🏠 00-Java ➡️ 引入接口与类之间的隔离
💬 评论